package com.kilman.logger.servlets;

import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.math.NumberUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gson.Gson;
import com.kilman.logger.log.ActiveLogs;
import com.kilman.logger.log.control.LogLines;
import com.kilman.logger.log.control.ManagedLog;
import com.kilman.logger.provider.ManagedLogsProvider;
import com.kilman.logger.provider.ManagedLogsUtils;

public class LoggerServlet extends HttpServlet {

    private static final long serialVersionUID = -6836372203184382349L;

    private static final String REGEX_SEPERATOR = ",";
    
    private ActiveLogs activeLogs;
    private WebApplicationContext context;
    private Gson gson;
    private ManagedLogsProvider managedLogsProvider;
       
    @Override
    public void init() {
        context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        activeLogs = context.getBean(ActiveLogs.class);
        gson = context.getBean(Gson.class);
        managedLogsProvider = context.getBean(ManagedLogsProvider.class);
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    @Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        setJSONContentType(response);

        String command = request.getParameter("command");
        
        if (command != null) {
            handleCommand(command, request, response);
        } else {
            handleInvalidCommand(request,response);
        }
	}
    
    private void handleCommand(String command, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (command.equals("endService")) {
            endService(request, response);
        } else if (command.equals("endAllServices")) {
            endAllServices(request, response);
        } else if (command.equals("killRemainings")) {
            killRemainings(request, response);
        } else  if (command.equals("managedLogs")) {
            managedLogs(request, response);
        } else if (command.equals("startLog")) {
            String rawStartLogIndex = request.getParameter("startLogIndex");
            startLog(request, response, rawStartLogIndex);
        } else if (command.equals("handleSession")) {
            handleSession(request, response);
        } else if (command.equals("getLogLines")) {
            String rawFromLine = request.getParameter("line");
            String rawIncludeRegex = request.getParameter("includeRegex");
            String rawExcludeRegex = request.getParameter("excludeRegex");
            getLogLines(request, response, rawFromLine, rawIncludeRegex, rawExcludeRegex);
        } else {
            handleInvalidCommand(command, request, response);
        }
    }
    
    private void handleSession(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        Status status = new Status();
        
        if (isConnected(request)) {
            status.setStatus("connected");
        } else {
            status.setStatus("not-connected");
        }
        
        writeStatus(response, status);
    }
    
    private void startLog(HttpServletRequest request, HttpServletResponse response, String rawStartLogIndex) throws ServletException, IOException {
        
        Map<Integer, ManagedLog> logs = getSessionManagedLogs(request);
        if (logs == null) {
            return; //TODO
        }
        
        Integer logIndex = getSessionLogIndex(request);
        
        if (logIndex != null && logs != null) {
            activeLogs.stopActiveLog(logs.get(logIndex));
        }

        int startLogIndex = NumberUtils.toInt(rawStartLogIndex);
        
        Status status = new Status();
        
        if (rawStartLogIndex == null) {
            status.setStatus("no-service-started");
        } else { 
        
            boolean shouldStartActiveLog = activeLogs.initActiveLog(logs.get(startLogIndex));
            
            if (shouldStartActiveLog) {
                activeLogs.startActiveLog(logs.get(startLogIndex));
                status.setStatus("sevice-started");
            } else {
                status.setStatus("service-not-started");
            }
        
            setSessionLogIndex(request, startLogIndex);
        }
        
        writeStatus(response, status);
    }
    
    private void getLogLines(HttpServletRequest request, HttpServletResponse response, 
            String rawFromLine, String rawIncludeRegex, String rawExcludeRegex) throws ServletException, IOException {

        setPlainContentType(response);
        
        if (!isConnected(request)) {
            return; // TODO
        }
        
        Map<Integer, ManagedLog> logs = getSessionManagedLogs(request);
        Integer logIndex = getSessionLogIndex(request);
        
        String[] includeRegex;
        if (rawIncludeRegex == null || rawIncludeRegex.equals("")) {
            includeRegex = null;
        } else {
            includeRegex = rawIncludeRegex.split(REGEX_SEPERATOR);
        }
        
        String[] excludeRegex;
        if (rawExcludeRegex == null || rawExcludeRegex.equals("")) {
            excludeRegex = null;
        } else {
            excludeRegex = rawExcludeRegex.split(REGEX_SEPERATOR);
        }
        
        int fromLine = NumberUtils.toInt(rawFromLine);
        
        LogLines logLines = activeLogs.getActiveLogLines(logs.get(logIndex), fromLine, true, includeRegex, excludeRegex);
        
        int preFilterSize = logLines.getPreFilterSize();
        response.addCookie(new Cookie("lineCount", Integer.toString(preFilterSize)));
        
        String logLinesOutput = logs.get(logIndex).generateOutput(logLines);
        
        response.getWriter().write(logLinesOutput);
    }
    
    private void managedLogs(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        Map<Integer, ManagedLog> managedLogs = managedLogsProvider.managedLogs();
        Map<Integer, String> managedLogsMap = ManagedLogsUtils.toWebMap(managedLogs);
        setSessionManagedLogs(request, managedLogs);
        
        String json = gson.toJson(managedLogsMap);

        response.getWriter().write(json);
        
    }
    
    private void endService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        if (!isConnected(request)) {
            return; //TODO
        }
        
        Map<Integer, ManagedLog> logs = getSessionManagedLogs(request);
        Integer logIndex = getSessionLogIndex(request);
        
        Status status = new Status();

        boolean serviceTerminated = activeLogs.stopActiveLog(logs.get(logIndex));

        if (serviceTerminated) {
            status.setStatus("service-terminated");
        } else {
            status.setStatus("service-not-terminated");
        }
        
        writeStatus(response, status);

        clearSession(request);
    }
    
    private void endAllServices(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        activeLogs.stopAllActiveLogs();
        writeStatus(response, new Status("ok"));
        clearSession(request);
    }

    private void killRemainings(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        activeLogs.killAllActiveLogs(managedLogsProvider);
        writeStatus(response, new Status("ok"));
        clearSession(request);
    }
    
    private void handleInvalidCommand(String command, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Status status = new Status();
        
        if (command == null) {
            status.setStatus("no-command-passed");
        } else {
            status.setStatus("unrecognized-command-" + command);
        }
        
        writeStatus(response, status);
    }
    
    private void handleInvalidCommand(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        handleInvalidCommand(null, request, response);
    }
    
    private void writeStatus(HttpServletResponse response, Status status) throws IOException {
        response.getWriter().write(gson.toJson(status));
    }
    
    private void setJSONContentType(HttpServletResponse response) {
        response.setContentType("application/json");
    }
    
    private void setPlainContentType(HttpServletResponse response) {
        response.setContentType("text/plain");
    }
    
    private Integer getSessionLogIndex(HttpServletRequest request) {
        return (Integer)request.getSession().getAttribute("logIndex");
    }
    
    private void setSessionLogIndex(HttpServletRequest request, Integer index) {
        request.getSession().setAttribute("logIndex", index);
    }
    
    @SuppressWarnings("unchecked")
    private Map<Integer, ManagedLog> getSessionManagedLogs(HttpServletRequest request) {
        return (Map<Integer, ManagedLog>)request.getSession().getAttribute("managedLogs");
    }
    
    private void setSessionManagedLogs(HttpServletRequest request, Map<Integer, ManagedLog> managedLogs) {
        request.getSession().setAttribute("managedLogs", managedLogs);
    }
    
    private boolean isConnected(HttpServletRequest request) {
        return (getSessionLogIndex(request) != null &&
                getSessionManagedLogs(request) != null);
    }
    
    private void clearSession(HttpServletRequest request) {
        setSessionLogIndex(request, null);
        setSessionManagedLogs(request, null);
    }
    
}
